package cn.ad.server.job.adapter.persistence.job;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;

import javax.annotation.Resource;

import cn.ad.server.job.adapter.dbaccess.AssembledCachedDataContainer;
import cn.ad.server.job.adapter.dbaccess.DbDataContainer;
import cn.ad.server.job.domain.job.StrategyWarmUpService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Repository;

import cn.ad.server.common.Constants;
import cn.ad.server.common.Constants.UpdateMode;
import cn.ad.server.domain.ad.DayTargert;
import cn.ad.server.domain.ad.Platform;
import cn.ad.server.domain.ad.Strategy;
import cn.ad.server.domain.ad.StrategyGeoMap;
import cn.ad.server.domain.ad.StrategyGroup;
import cn.ad.server.domain.ad.StrategyPlatformMap;

@Repository
public class StrategyWarmUpServant implements StrategyWarmUpService {

	private static Logger logger = LoggerFactory.getLogger(StrategyWarmUpServant.class);
	
	@Resource(name = "cacheRedisTemplate")
	private ValueOperations<String, String> stringValueOperations;

	@Resource(name = "cacheRedisTemplate")
	private ValueOperations<String, Strategy> strategyOperations;
	
	@Resource(name = "cacheRedisTemplate")
	private ValueOperations<String, Platform> platformOperations;
	
	@Resource(name = "cacheRedisTemplate")
	private RedisTemplate<String, String> redisTemplate;
	
	@Autowired
	private AssembledCachedDataContainer container;
	
	//TODO 策略-平台的map 和 策略组-平台的map 是否都是物理删除。  
	/**
	 * 缓存platformId和StrategiesMap的关系。
	 */
	public void cachedPlatformStrategies(){
		/**
		 * 全表-copy StrategyPlatformMap表
		 */
		List<StrategyPlatformMap> strategyPlatformMaps = DbDataContainer.getStrategyPlatformMapAll();
		/**
		 * 全表-copy StrategyGroupPlatformMap表对应的StrategyPlatformMap对象。
		 */
		List<StrategyPlatformMap> strategyPlatformMaps2 = DbDataContainer.getStrategyGroupPlatformMapAll();
		
		if(strategyPlatformMaps==null){
			return;
		}
		
		if (strategyPlatformMaps2!=null) {
			strategyPlatformMaps.addAll(strategyPlatformMaps2);
		}
		ConcurrentHashMap<Integer, CopyOnWriteArraySet<Strategy>> platformStrategiesMap = container.getPlatformStrategiesMap();//存放有效的key（平台Id）对应有效的策略对象。
		
		if(Constants.UPDATE_MUDO==UpdateMode.physicsMode){
			platformStrategiesMap.clear();
		}
		long currentTimeMillis = System.currentTimeMillis();
		for (int i = 0; i < strategyPlatformMaps.size(); i++) {
			StrategyPlatformMap mapper = strategyPlatformMaps.get(i);
			platformStrategiesMap.putIfAbsent(mapper.getPlatformId(),  new CopyOnWriteArraySet<Strategy>());
			if(mapper.getStatus()==1){
				Strategy s = getStrategyFromLocal(mapper.getStrategyId());//返回所有的策略。
				CopyOnWriteArraySet<Strategy> copyOnWriteArraySet = platformStrategiesMap.get(mapper.getPlatformId());
				copyOnWriteArraySet.remove(s);
				if(s!=null&&s.getStatus()==1){
					if(s.getCampaign()!=null&&s.getEndTime().getTime()>currentTimeMillis){//当订单有效.且订单策略都没有投放结束					
						s = attachStrategyAssociation(s);
						copyOnWriteArraySet.add(s);
						upToRedis(mapper.getPlatformId(), s, true,0);
					}
				}else{
				}
			}else{
				//TODO 策略-平台的map 和 策略组-平台的map 是否都是物理删除。  现在是全表查询。可以去掉。
				//platformStrategiesMap.get(mapper.getPlatformId()).remove(getStrategyFromLocal(mapper.getStrategyId()));
				//upToRedis(mapper.getPlatformId(),null, true,mapper.getStrategyId());
			}
		}
	}
	private  ConcurrentHashMap<Long, Strategy> strategyMapAll;

	/**
	 * 从局部缓存。获取
	 * @param id 
	 * @return
	 */
	private Strategy getStrategyFromLocal(Long id){
		return strategyMapAll.get(id);
	}
	
	/**
	 * 局部变量缓存。
	 * @return
	 */
	private void cachedStrategyLocal(){
		strategyMapAll = DbDataContainer.getStrategyMapAll();
//		Collection<Strategy> values = strategyMapAll.values();
//		for (Strategy strategy : values) {
//			if(strategy.getSpecifyCreativeIds()!=null&&strategy.getSpecifyCreativeIds().size()>0){
//				System.out.println(strategy.getId());
//			}
//		}
	}
	
	public void warmUpPlatformStrategies(){
		mapStrategyGroupAll = container.getMapStrategyGroupAll();
		//执行DbDataContainer.getStrategyMapAll()。注意：不是拷贝
		cachedStrategyLocal();
		
		cachedPlatformStrategies();
		// 策略组中填充当天可以投放时间 --top
		groupAndPopulateByStrategyGroup();
		
		filtStrategy(mapStrategyGroupAll);
		
		computeStrategyGroupUsableHours(mapStrategyGroupAll);
		// 策略组中填充当天可以投放时间 --bot
		
		ConcurrentHashMap<Integer, CopyOnWriteArraySet<Strategy>> platformStrategiesMap = container.getPlatformStrategiesMap();
		List<Platform> platformAll = DbDataContainer.getPlatformAll();//全表遍历。保证redis中存放的是有效的数据。无效的删除。
		for (Platform platform : platformAll) {
			if(platform.getStatus()==1||platform.getStatus()==2){
				platform.setStrategies(platformStrategiesMap.get(platform.getId()));
				platformOperations.set(platform.toCacheKey(), platform);
			}else{
				redisTemplate.delete(platform.toCacheKey());
			}
		}
		
	}
	
	@Value("${cached.redisDebug:false}")
	private boolean redisDebug;
	
	void upToRedis(Integer pId,Strategy s,boolean operatorFlg,Integer sId){
		if(!redisDebug){
			return;
		}
		String key = "";
		if(s==null){
			key = "StrategiesByPlatformId:"+pId+":"+sId;
		}else{			
			key = "StrategiesByPlatformId:"+pId+":"+s.getId();
		}
		if(operatorFlg){
			strategyOperations.set(key, s);
		}else{
			redisTemplate.delete(key);
		}
	}
	
	void upToRedis(Strategy s,boolean operatorFlg){
		if(!redisDebug){
			return;
		} 
		String key = s.toCacheKey();
		if(operatorFlg){			
			strategyOperations.set(key, s);
		}else{
			redisTemplate.delete(key);
		}
	}
	
	public void cachedStrategy() {
		List<Strategy> mapStrategys = DbDataContainer.getStrategyAll();
		if(mapStrategys==null){
			return;
		}
		for (Strategy s:mapStrategys) {
			try {
				if (s.getStatus() == 1) {
					s = attachStrategyAssociation(s);
					container.getStrategies().put(s.getId(), s);
					upToRedis(s, true);
				} else if (s.getStatus() != 1) {
					container.getStrategies().remove(Long.valueOf(s.getId()));
					upToRedis(s, false);
				}
			} catch (Exception e) {
				if (s != null) {
					logger.error("错误：在获取strategy_id =" + s.getId() + "数据时发生错误 \n 错误信息：", e);
				}
			}
		}
	}
	
	public void warmUpStrategy() {
		List<Strategy> mapStrategys = DbDataContainer.getStrategyAll();
		if(mapStrategys==null){
			return;
		}
		for (Strategy s:mapStrategys) {
			try {
				if (s.getStatus() == 1) {
					s = attachStrategyAssociation(s);
					strategyOperations.set(s.toCacheKey(), s);
				} else if (s.getStatus() != 1) {
					redisTemplate.delete(s.toCacheKey());
				}
			} catch (Exception e) {
				if (s != null) {
					logger.error("错误：在获取strategy_id =" + s.getId() + "数据时发生错误 \n 错误信息：", e);
				}
			}
		}
	}

	/**
	 * StrategyGroup 过滤掉无效的Strategy
	 * @param s
	 */
	private void filtStrategy(ConcurrentHashMap<Integer, StrategyGoupMap> strategyGoupMaps) {
		Collection<StrategyGoupMap> StrategyGoupMaps = strategyGoupMaps.values();
		for (StrategyGoupMap strategyGoupMap : StrategyGoupMaps) {
			StrategyGroup group = strategyGoupMap.getGroup();
//			if(group.getStatus()!=1){
//				continue;
//			}
			List<Strategy> strategys = strategyGoupMap.getList();
			List<Strategy> strategysOk = new ArrayList<>();
			for (Strategy strategy :strategys) {
				if(strategy.getStatus()!=1){
					continue;
				}
				if(strategy.getDayTargert()==null){
					addDayTarget(strategy);
				}
				strategysOk.add(strategy);
			}
			strategyGoupMap.setList(strategysOk);
		}
	}
	/**
	 * 得到当天和下一天的策略组的可用时间。
	 */
	private void computeStrategyGroupUsableHours(ConcurrentHashMap<Integer, StrategyGoupMap> strategyGoupMaps) {
		Collection<StrategyGoupMap> StrategyGoupMaps = strategyGoupMaps.values();
		Calendar calendar = Calendar.getInstance();
		Calendar nextDayCalendar = Calendar.getInstance();
		nextDayCalendar.add(Calendar.HOUR, 24);
		long currentTime = calendar.getTime().getTime();
		long nextDayTime = nextDayCalendar.getTime().getTime();
		int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK) - 1;
		int nextDayOfWeek = nextDayCalendar.get(Calendar.DAY_OF_WEEK) - 1;
		
		for (StrategyGoupMap strategyGoupMap : StrategyGoupMaps) {
			StrategyGroup group = strategyGoupMap.getGroup();
			if(group.getStatus()!=1){
				continue;
			}
//			if(group.getId()==33){
//				System.out.println("dddd");
//			}
			List<Strategy> listStrategy = strategyGoupMap.getList();
			int result = 0;
			int nextDayResult = 0;
			for (Strategy strategy : listStrategy) {
				long startTime = strategy.getStartTime().getTime();
				long endTime = strategy.getEndTime().getTime();
				if(startTime<=currentTime&&currentTime<endTime){					
					result = strategy.getDayTargert().toArray()[dayOfWeek]|result;
				}
				if(startTime<=nextDayTime&&nextDayTime<endTime){					
					nextDayResult = strategy.getDayTargert().toArray()[nextDayOfWeek]|nextDayResult;
				}
			}
			
			int currentDayUsableHour=0;
			int nextDayUsableHour=0;
			for (int i = 0; i < 24; i++) {
				if((result&(1<<i))>0){
					currentDayUsableHour++;
				}
				if((nextDayResult&(1<<i))>0){
					nextDayUsableHour++;
				}
			}
			group.putUsablehour(calendar.getTime(), currentDayUsableHour);
			group.putUsablehour(nextDayCalendar.getTime(), nextDayUsableHour);
			
			group.addUsableHour(calendar.getTime(), currentDayUsableHour);
			group.addUsableHour(nextDayCalendar.getTime(), nextDayUsableHour);
			
			group.putDayTargetMap(calendar.getTime(), result);
			group.putDayTargetMap(nextDayCalendar.getTime(), nextDayResult);
		
		}
	}
	
	/**
	 * 根据策略组分组。
	 * 设定策略中的策略组
	 * @param s
	 */
	private void groupAndPopulateByStrategyGroup() {
		mapStrategyGroupAll.clear();
		Collection<Strategy> mapStrategys = strategyMapAll.values();
		for (Strategy strategy : mapStrategys) {
			StrategyGroup strategyGroup = strategy.getStrategyGroup();
			if(strategyGroup!=null){
				StrategyGoupMap strategyGoupMap = mapStrategyGroupAll.get(strategyGroup.getId());
				if(strategyGoupMap==null){
					strategyGoupMap = new StrategyGoupMap();
					strategyGoupMap.setGroup(strategyGroup);
					mapStrategyGroupAll.put(strategyGroup.getId(), strategyGoupMap);
				}else{
					strategyGroup = strategyGoupMap.getGroup();
				}
				strategyGoupMap.add(strategy);
				strategy.setStrategyGroup(strategyGroup);
			}
		}
	}
	
	/**
	 * 一个组对应多个策略。
	 * @author wangwei
	 *
	 */
	public final class StrategyGoupMap{
		public StrategyGroup group;
		public List<Strategy> list= new ArrayList<>();
		
		public StrategyGroup getGroup() {
			return group;
		}
		public void setGroup(StrategyGroup group) {
			this.group = group;
		}
		public List<Strategy> getList() {
			return list;
		}
		public void setList(List<Strategy> list) {
			this.list = list;
		}
		public void add(Strategy s) {
			this.list.add(s);
		}
	}
	
	/**
	 * 临时存放数据。
	 * key:StrategyGoupId
	 * value:StrategyGoupMap
	 */
	private ConcurrentHashMap<Integer, StrategyGoupMap> mapStrategyGroupAll;
	
	
	/**
	 * 包含定向。
	 * @param s
	 * @return
	 */
	private Strategy attachStrategyAssociation(Strategy s) {
		if(s==null){
			return null;
		}
		Strategy strategy = s;
		long strategyId = strategy.getId();

		try {
			/**
			 * Crowd
			 */
			strategy.setCrowdTargert(DbDataContainer.getStrategyCrowdMapByStrategyId(strategyId));
			
			/**
			 * Device
			 */
			strategy.setDeviceTargert(DbDataContainer.getStrategyDeviceMapByStrategyId(strategyId));
			/**
			 * 地域定向
			 */
			List<Integer> countrys = new ArrayList<>();
			strategy.setGeoCountryTargert(countrys);
			List<Integer> states = new ArrayList<>();
			strategy.setGeoStateTargert(states);
			List<Integer> citys = new ArrayList<>();
			strategy.setGeoCityTargert(citys);
			List<Integer> districts = new ArrayList<>();
			strategy.setGeoDistrictTargert(districts);

			List<StrategyGeoMap> geos = DbDataContainer.getStrategyGeoMaps(strategyId);
			if (geos != null) {
				for (StrategyGeoMap geoMap : geos) {
					int type = geoMap.getGeoType();
					int geoId = geoMap.getGeoId();
					// 0 国家 对应geo_country， 1 省/州 对应geo_state, 2 城市 对应geo_cidy, 3
					// 省份分区 对应geo_district',
					if (type == 0) {
						countrys.add(geoId);
					} else if (type == 1) {
						states.add(geoId);
					} else if (type == 2) {
						citys.add(geoId);
					} else if (type == 3) {
						districts.add(geoId);
					}
				}
			}

			/**
			 * 时间定向 当没有数据给定默认。当获取到DayTargert中全为0更改为全投
			 */
			addDayTarget(strategy);
			
		} catch (Exception e) {
			logger.error("错误: 执行strategy_Id为：" + strategyId + "的策略对象时，发生错误。");
			throw e;
		}
		return strategy;
	}
	
	/**
	 * 添加时间定向。 FIXME 在adSpaceRepoositoryImpl中有重复
	 * 
	 * @param strategy
	 */
	private void addDayTarget(Strategy strategy) {
		if (strategy == null) {
			return;
		}
		/**
		 * 时间定向
		 */
		List<DayTargert> dayTargerts = DbDataContainer.getDayTargerts(strategy.getId());
		if (dayTargerts!=null&&dayTargerts.size() > 0) {
			DayTargert dayTargert = dayTargerts.get(0);

			if (dayTargert.getMonday() == 0 && dayTargert.getTuesday() == 0 && dayTargert.getWednesday() == 0
					&& dayTargert.getThursday() == 0 && dayTargert.getFriday() == 0 && dayTargert.getSaturday() == 0
					&& dayTargert.getSunday() == 0) {
				int allHours = 16777215;
				dayTargert.setMonday(allHours);
				dayTargert.setTuesday(allHours);
				dayTargert.setWednesday(allHours);
				dayTargert.setThursday(allHours);
				dayTargert.setFriday(allHours);
				dayTargert.setSaturday(allHours);
				dayTargert.setSunday(allHours);
				strategy.setDayTargert(dayTargert);
			}
			strategy.setDayTargert(dayTargert);
		} else {
			// 如果没有则默认一周这个时间段全投放
			DayTargert dayTargert = new DayTargert();
			int allHours = 16777215;
			dayTargert.setMonday(allHours);
			dayTargert.setTuesday(allHours);
			dayTargert.setWednesday(allHours);
			dayTargert.setThursday(allHours);
			dayTargert.setFriday(allHours);
			dayTargert.setSaturday(allHours);
			dayTargert.setSunday(allHours);
			strategy.setDayTargert(dayTargert);
		}
	}


}
